package graphIndexerPackage;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.Adler32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.swing.SwingUtilities;
import javax.xml.stream.Location;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLReporter;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;


public class IndexerXML 
{
	
	private static int MAX_PHASE = 5;

	private XMLInputFactory factory  ;
	private String XMLfile  ;
	private Adler32 adler32 = new Adler32();
	 
	int cptVille = 0 ;
	int cptRoute = 0;
	 
	private HashMap<String, FileWriter> indexVilles = new HashMap<String, FileWriter>();
	private List<String> indexRoutes = new LinkedList<String>();
	
	private class MakeProgress implements Runnable {

		private int phase = 0;
		private int maxphase = 0;
		private int current = 0;
		private int maxcurrent = 0;
		private IndexerProgress index = null;
		
		public MakeProgress( IndexerProgress indexerProgress, int phase, int maxphase, int current, int maxcurrent ) {
			this.phase = phase;
			this.maxphase = maxphase;
			this.current = current;
			this.maxcurrent = maxcurrent;
			this.index = indexerProgress;
		}
		
		public void run() {
			index.progress(phase, maxphase, current, maxcurrent);
		}		
	}
	
	private IndexerProgress indexProgress = null;
	 	 
	public IndexerXML(String fichier )
	{
		XMLfile = fichier ;
	}
	
	public void start() throws IndexerXmlException {
		factory = XMLInputFactory.newInstance();
		factory.setXMLReporter(
				new XMLReporter() 
				{
		          	public void report(String message, String typeErreur, Object source, Location location) throws XMLStreamException 
		          	{
		          		System.err.println("Erreur de type : " + typeErreur + ", message : " + message);
		          	}
				}
		);
		
		File file = new File( XMLfile );
		XMLStreamReader reader1;
		XMLStreamReader reader2;
		
		if ( indexProgress != null ) {
			SwingUtilities.invokeLater( new MakeProgress( indexProgress, 0, MAX_PHASE, 0, 100) );
		}
		
		try 
		{
			System.out.print("Computing number of elements...");
			reader2 = factory.createXMLStreamReader(new FileReader(file));
		    while (reader2.hasNext()) 
		    {
		        int type = reader2.next();
		        if (type == XMLStreamReader.START_ELEMENT)
		        {
	        		if( reader2.getLocalName().equals("ville")) ++cptVille;
	        		if( reader2.getLocalName().equals("route"))	++cptRoute;
		        }
		    }
		    reader2.close();
		    System.out.println("ok");
		    System.out.println("Reading file : " + file.getAbsolutePath());
			reader1 = factory.createXMLStreamReader(new FileReader(file));
			
			int currentCptVille = 0;
			int currentCptRoute = 0;
			
		    while (reader1.hasNext()) 
		    {
		        int type = reader1.next();
		        if (type == XMLStreamReader.START_ELEMENT)
		        {
	        		if( reader1.getLocalName().equals("ville")) {
	        			ecrireVille(reader1);
	        			if ( indexProgress != null ) {
	        				SwingUtilities.invokeLater( new MakeProgress( indexProgress, 1, MAX_PHASE, ++currentCptVille, cptVille) );
	        			}
	        		}
	        		if( reader1.getLocalName().equals("route")) {
	        			ecrireRoute(reader1);
	        			if ( indexProgress != null ) {
	        				SwingUtilities.invokeLater( new MakeProgress( indexProgress, 2, MAX_PHASE, ++currentCptRoute, cptRoute) );
	        			}
	        		}
		        }
		    }
		    reader1.close();
		}
		catch (FileNotFoundException e) {e.printStackTrace();}
		catch (XMLStreamException e) 	{e.printStackTrace();}
		
		finaliserFichiersVille(3);
		//Computing Adler32
		System.out.print("Computing CRC32...");
		try {
			long currentlen = 0;
			long maxlen = file.length();
			
			FileInputStream frCRC = new FileInputStream(file);
			byte[] buffer = new byte[1024];
			int len = 0;
			while ( true ) {
				len = frCRC.read(buffer);
				if ( indexProgress != null ) {
        			if ( indexProgress != null ) {
        				SwingUtilities.invokeLater( new MakeProgress( indexProgress, 4, MAX_PHASE, (int)++currentlen, (int)maxlen) );
        			}
				}
				if (len == -1 ) break;
				adler32.update(buffer, 0, len);
			}
			frCRC.close();
			System.out.println("ok");
		} 
		catch (FileNotFoundException e) {
			throw new IndexerXmlException(e.getMessage());
		}
		catch (IOException e) 			{
			throw new IndexerXmlException(e.getMessage());
		}
		
		String fileS = file.getAbsolutePath();
		int i = fileS.lastIndexOf(".");
		String s = fileS.substring(0,i);
		compress( s+".zip", 5 );
		if ( indexProgress != null ) {
			indexProgress.progressFinish();
		}
	}

	private void finaliserFichiersVille(int phase) {
		System.out.print("Flushing buffers...");
		try {
			int currentCptVille = 0;
			for (String nom : indexVilles.keySet()) {
				indexVilles.get(nom).write("</ville>");
				indexVilles.get(nom).close();
				indexVilles.put(nom, null);
				if ( indexProgress != null ) indexProgress.progress(phase, MAX_PHASE, ++currentCptVille, cptVille);
			}
			System.out.println("ok");
		} catch (IOException e) {
			System.out.println("error");
			e.printStackTrace();
		}

	}

	private void compress(String fileName, int phase) {
		System.out.print("Compressing...");
		File f = new File(fileName);
		try {
			FileOutputStream fs = new FileOutputStream(f);
			ZipOutputStream zipfs = new ZipOutputStream(fs);
			zipfs.setLevel(6);
			
			int currentCpt = 0;

			zipfs.putNextEntry(new ZipEntry("ADLER32"));
			String crcstr = "" + adler32.getValue();
			zipfs.write(crcstr.getBytes());
			zipfs.closeEntry();

			byte[] buffer = new byte[1024];

			for (String s : indexVilles.keySet()) {
				zipfs.putNextEntry(new ZipEntry("V_"+s));

				File fz = new File(s + ".xml");
				FileInputStream fr = new FileInputStream(fz);

				int len = 0;
				while (true) {
					len = fr.read(buffer);
					if (len == -1) break;
					zipfs.write(buffer, 0, len);
				}
				fr.close();
				fz.delete();
				zipfs.closeEntry();
				if ( indexProgress != null ) indexProgress.progress(phase, MAX_PHASE, ++currentCpt, cptVille+cptRoute);
			}
			for (String s : indexRoutes) {
				zipfs.putNextEntry(new ZipEntry("R_"+s));
				File fz = new File(s + ".xml");
				FileInputStream fr = new FileInputStream(fz);

				int len = 0;
				while (true) {
					len = fr.read(buffer);
					if (len == -1)
						break;
					zipfs.write(buffer, 0, len);
				}
				fr.close();
				fz.delete();
				zipfs.closeEntry();
				if ( indexProgress != null ) indexProgress.progress(phase, MAX_PHASE, ++currentCpt, cptVille+cptRoute);
			}
			zipfs.close();
			System.out.println("ok");
		} catch (FileNotFoundException e) {
			System.out.println("error");
			e.printStackTrace();
		} catch (IOException e) {
			System.out.println("error");
			e.printStackTrace();
		}

	}

	private void appendTronconInXMLfile(FileWriter fw,
			HashMap<String, String> tronconCourant) {
		try {
			fw.write("<troncon>");
			for (String clef : tronconCourant.keySet()) {
				fw.write("<" + clef + ">");
				fw.write(tronconCourant.get(clef));
				fw.write("</" + clef + ">");
			}
			fw.write("</troncon>");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void appendTronconVille(HashMap<String, String> tronconCourant) {
		appendTronconInXMLfile(indexVilles.get(tronconCourant.get("ville1")),
				tronconCourant);
		appendTronconInXMLfile(indexVilles.get(tronconCourant.get("ville2")),
				tronconCourant);
	}

	private void ecrireRoute(XMLStreamReader reader) {
		try {
			XMLOutputFactory xof = XMLOutputFactory.newInstance();
			XMLStreamWriter xtw = null;

			boolean inNom = false;
			boolean inTroncon = false;

			HashMap<String, String> tronconCourant = null;
			String baliseCourante = "";
			String nom = "";
			FileWriter fw = null;
			boolean init = false;

			int type = reader.getEventType();
			while (!(type == XMLStreamReader.END_ELEMENT && reader
					.getLocalName().equals("route"))) {
				if (type == XMLStreamReader.START_ELEMENT) {
					inNom = (reader.getLocalName().equals("nom"));

					if (reader.getLocalName().equals("troncon")) {
						inTroncon = true;
						tronconCourant = new HashMap<String, String>();
					}
					baliseCourante = reader.getLocalName();
					if (init)
						xtw.writeStartElement(baliseCourante);
				}

				if ((type == XMLStreamReader.CDATA || type == XMLStreamReader.CHARACTERS)) {
					if (inNom) {
						nom = reader.getText().trim();
						fw = new FileWriter(nom + ".xml");
						xtw = xof.createXMLStreamWriter(fw);
						xtw.writeStartDocument();
						xtw.writeStartElement("route");
						xtw.writeStartElement("nom");
						init = true;
					}
					if (init)
						xtw.writeCharacters(reader.getText().trim());
					if (inTroncon && !baliseCourante.equals("")
							&& !baliseCourante.equals("troncon")) {
						tronconCourant.put(baliseCourante, reader.getText()
								.trim());
					}
				}

				if (type == XMLStreamReader.END_ELEMENT) {
					if (reader.getLocalName().equals("troncon")) {
						inTroncon = false;
						tronconCourant.put("route", nom);
						appendTronconVille(tronconCourant);
						tronconCourant.clear();
					}
					if (init)
						xtw.writeEndElement();
					inNom = false;
					baliseCourante = "";
				}
				type = reader.next();
			}
			indexRoutes.add(nom);

			xtw.writeEndDocument();
			xtw.flush();
			xtw.close();
			fw.close();
		} catch (XMLStreamException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void ecrireVille(XMLStreamReader reader) {
		try {
			FileWriter fw = null;
			boolean inNom = false;
			String nom = "";

			boolean init = false;
			String baliseCourante = null;

			int type = reader.getEventType();
			while (!(type == XMLStreamReader.END_ELEMENT && reader
					.getLocalName().equals("ville"))) {
				if (type == XMLStreamReader.START_ELEMENT) {
					inNom = reader.getLocalName().equals("nom");
					baliseCourante = reader.getLocalName();
					if (init)
						fw.write("<" + baliseCourante + ">");
				}

				if ((type == XMLStreamReader.CDATA || type == XMLStreamReader.CHARACTERS)) {
					if (inNom) {
						nom = reader.getText().trim();
						fw = new FileWriter(nom + ".xml", true);
						fw.write("<?xml version=\"1.0\" ?><ville><nom>");
						init = true;
					}
					if (init)
						fw.write(reader.getText().trim());
				}

				if (type == XMLStreamReader.END_ELEMENT) {
					if (init)
						fw.write("</" + reader.getLocalName() + ">");
					inNom = false;
				}

				type = reader.next();
			}
			indexVilles.put(nom, fw);
		} catch (XMLStreamException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public void registerProgress(IndexerProgress indexProgress) {
		this.indexProgress = indexProgress;
	}
}
